#include "led_device.h"

#include <string.h>

#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_log.h"

using namespace std;


void LED_Status_t::init(uint32_t pin_num)
{
    gpio_reset_pin((gpio_num_t)pin_num);
    gpio_set_direction((gpio_num_t)pin_num, GPIO_MODE_OUTPUT);
    gpio_set_level((gpio_num_t)pin_num, !logic);

    pin = pin_num;
    mode = 0;
    status = 0;
    off_time = 0;
    on_time = 0;
    counter = 0;
    logic = 0;
}

LED_Status_t * LED_Device::get_pin(uint32_t pin)
{
    if (this->pin_sta_list.empty()) {
        return nullptr;
    }
    LED_Status_t *led = nullptr;
    for(vector<LED_Status_t *>::iterator iter = this->pin_sta_list.begin(); iter != this->pin_sta_list.end(); iter++) {
        if ((**iter).pin == pin) {
            led = (*iter);
            return led;
        }
    }

    return nullptr;
}

int32_t LED_Device::register_pin(uint32_t pin, uint8_t logic)
{
    if (get_pin(pin)) {
        return 1;
    }

    LED_Status_t *led = new LED_Status_t();
    led->init(pin);
    led->logic = logic;
    this->pin_sta_list.push_back(led);
    return 0;
}

int32_t LED_Device::register_pin(LED_Status_t *led_status)
{
    if (get_pin(led_status->pin)) {
        return 1;
    }
    LED_Status_t *led = new LED_Status_t();
    memcpy(led, led_status, sizeof(LED_Status_t));
    this->pin_sta_list.push_back(led);
    return 0;
}

int32_t LED_Device::unenroll_pin(uint32_t pin)
{
    if (get_pin(pin)) {
        return -1;
    }
    vector<LED_Status_t *>::iterator iter = this->pin_sta_list.begin();
    LED_Status_t *led = nullptr;
    for(; iter != this->pin_sta_list.end(); iter++) {
        if ((**iter).pin == pin) {
            led = (*iter);
            break;
        }
    }

    if (led) {
        this->pin_sta_list.erase(iter);
    }
    delete led;
    led = nullptr;
    return 0;
}


int32_t LED_Device::upadte_pin(uint32_t pin, uint8_t mode, uint16_t on_time, uint16_t off_time)
{
    LED_Status_t *led = get_pin(pin);
    if (!led) {
        return -1;
    }
    led->mode = mode;
    led->off_time = off_time / LED_COUNT_PERIOD;
    led->on_time = on_time / LED_COUNT_PERIOD;
    led->hb_time = LED_HB_TIME_DEF / LED_COUNT_PERIOD;
    led->counter = 0;
    return 0;
}

int32_t LED_Device::mode_set(uint32_t pin, uint8_t mode)
{
    LED_Status_t *led = get_pin(pin);
    if (!led) {
        return -1;
    }
    led->mode = mode;
    return 0;
}


void LED_Device::scan(LED_Status_t *led_status)
{
    if (LED_MODE_OFF == led_status->mode) {
        if (LED_STATUS_OFF != led_status->status) {
            led_status->status = LED_STATUS_OFF;
            gpio_set_level((gpio_num_t)led_status->pin, get_pin_value(led_status->logic, LED_STATUS_OFF));
        }
    } else if (LED_MODE_ON == led_status->mode) {
        if (LED_STATUS_ON != led_status->status) {
            led_status->status = LED_STATUS_ON;
            gpio_set_level((gpio_num_t)led_status->pin, get_pin_value(led_status->logic, LED_STATUS_ON));
        }
    } else if (LED_MODE_FLICKER == led_status->mode) {
        if (LED_STATUS_OFF == led_status->status) {
            led_status->counter ++;
            if (led_status->counter >= led_status->off_time) {
                // ESP_LOGI(pcTaskGetName(nullptr), "LED_MODE_FLICKER [%ld] LED_STATUS_OFF", led_status->pin);
                led_status->counter = 0;
                gpio_set_level((gpio_num_t)led_status->pin, get_pin_value(led_status->logic, LED_STATUS_ON));
                led_status->status = LED_STATUS_ON;

            }
        } else if (LED_STATUS_ON == led_status->status) {
            led_status->counter ++;
            if (led_status->counter >= led_status->on_time) {
                // ESP_LOGI(pcTaskGetName(nullptr), "LED_MODE_FLICKER [%ld] LED_STATUS_ON", led_status->pin);
                led_status->counter = 0;
                gpio_set_level((gpio_num_t)led_status->pin, get_pin_value(led_status->logic, LED_STATUS_OFF));
                led_status->status = LED_STATUS_OFF;
            }
        } else {
            led_status->status = LED_STATUS_OFF;
        }
    } else if (LED_MODE_HEARTBEATE == led_status->mode) {
        led_status->counter ++;
        switch(led_status->next_sta) {
            case 0:
            if (LED_STATUS_ON != led_status->status) {
                led_status->status = LED_STATUS_ON;
                gpio_set_level((gpio_num_t)led_status->pin, get_pin_value(led_status->logic, LED_STATUS_ON));
            }
            if (led_status->counter >= led_status->on_time) {
                led_status->next_sta = 1;
                led_status->counter = 0;
            }
            break;
            case 1:
            if (LED_STATUS_OFF != led_status->status) {
                led_status->status = LED_STATUS_OFF;
                gpio_set_level((gpio_num_t)led_status->pin, get_pin_value(led_status->logic, LED_STATUS_OFF));
            }
            if (led_status->counter >= led_status->off_time) {
                led_status->next_sta = 2;
                led_status->counter = 0;
            }
            break;
            case 2:
            if (LED_STATUS_ON != led_status->status) {
                led_status->status = LED_STATUS_ON;
                gpio_set_level((gpio_num_t)led_status->pin, get_pin_value(led_status->logic, LED_STATUS_ON));
            }
            if (led_status->counter >= led_status->on_time) {
                led_status->next_sta = 3;
                led_status->counter = 0;
            }
            break;
            case 3:
            if (LED_STATUS_OFF != led_status->status) {
                led_status->status = LED_STATUS_OFF;
                gpio_set_level((gpio_num_t)led_status->pin, get_pin_value(led_status->logic, LED_STATUS_OFF));
            }
            if (led_status->counter >= (led_status->hb_time ) - ((led_status->on_time * 2) + led_status->off_time)) {
                led_status->counter = 0;
                led_status->next_sta = 0;
            }
            break;
            default:
            break;
        }
    } else {
        led_status->status = LED_STATUS_OFF;
        gpio_set_level((gpio_num_t)led_status->pin, get_pin_value(led_status->logic, LED_STATUS_OFF));
    }
}

uint8_t LED_Device::get_pin_value(uint8_t logic, LED_Status_e status)
{
    if (status) {
        return logic == 1 ? 1 : 0;
    } else {
        return logic == 1 ? 0 : 1;
    }
}

void LED_Device::process(void *params)
{
    LED_Device *device = (LED_Device *)params;
    static TickType_t xLastWakeTime = 0;
    xLastWakeTime = xTaskGetTickCount();
    ESP_LOGI("LED_Device", "LED_Device::process start");
    for(;;) {
        xTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(LED_COUNT_PERIOD));
        LED_Status_t *led = nullptr;
        for(vector<LED_Status_t *>::iterator iter = device->pin_sta_list.begin(); iter != device->pin_sta_list.end(); iter++) {
            led = (*iter);
            device->scan(led);
        }
    }
}


int32_t LED_Device::run(void)
{
    return xTaskCreatePinnedToCore(process, "led_proc", 2048, this, 3, NULL, 1);
}